Richard’s Weblog

March 10, 2009

StringTemplate, a brief example

Filed under: template system,Web development — Richard @ 3:04 am
Tags: ,

Introduction

I already wrote about a web templating system named Tiles. This leaded me to search for other java template systems, and I stumbled across StringTemplate.

StringTemplate is a template engine that defines its own language grammar. It enforce the separation of the display logic from the business logic, in example by disallowing variable assignments in that display logic. As an analogy, JSP, even if correctly used with JSTL and a separated controller, will always make it possible for programmers to modify the model, call a service, query a database etc. StringTemplate forbids any assignments, making it harder to put what should be business or controller logic in the view.

Here is a brief example of using StringTemplate in a j2ee environment. It is not meant to be a real life example, but more a proof of concept. The example consist of a welcome and about page, each one using the same layout.

Configuration

dependencies

There is not much dependencies to add to be able to use StringTemplate :

  • stringtemplate.jar
  • antlr-2.7.7.jar

I took those files in the lib directory of the StringTemplate 3.2 distribution. They are in the /WEB-INF/lib directory of my web application.

Servlet (web.xml)

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">
  
  <servlet>  
    <servlet-name>StringTemplateServlet</servlet-name>  
    <servlet-class>sample.StringTemplateServlet</servlet-class>  
  </servlet>  

  <servlet-mapping>  
    <servlet-name>StringTemplateServlet</servlet-name>  
    <url-pattern>*.st</url-pattern>
  </servlet-mapping> 

</web-app>

Implementation

The servlet

package sample;

import javax.servlet.*;
import javax.servlet.http.*;

import org.antlr.stringtemplate.*;
import java.io.*;

public class StringTemplateServlet extends HttpServlet {
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
    PrintWriter out = response.getWriter();
    ServletContext context = getServletContext();
    String templatePath = context.getRealPath("/WEB-INF");
    StringTemplateGroup group = new StringTemplateGroup("underwebinf", templatePath);
    // removes the leading "/" and the trailing ".st"    
    String servletPath = request.getServletPath().substring(1,request.getServletPath().lastIndexOf(".st"));
    StringTemplate page = group.getInstanceOf(servletPath);
    out.print(page);
  }
}

The servlet specify that all template files will be found under the WEB-INF directory. The name of the .st file that will be called is the one specified in the request. In example, the URL http://localhost:8080/stringtemplate/home.st would point to the /WEB-INF/home.st template file.

The home page (/WEB-INF/home.st)

The home page represent a view in itself. It must use the template layout :

$defaultLayout(body=welcome())$

This single line of StringTemplate code tells StringTemplate to execute defaultLayout.st, and to replace it’s $body$ attribute with the result of the welcome.st template file execution. I can therefore reference other templates by their name, stripping the “.st” part.

The layout (/WEB-INF/layout.st)

The general layout is made to be shared by web pages. It defines the general look of a page :

<html>
  <body style="width:100%;height:100%">
    <table border="1" cellspacing="0" cellpadding="0" style="width:100%;height:100%">
      <tr>
        <td colspan="2">
          $header()$
        </td>
      </tr>
      <tr>
        <td>
          $menu()$
        </td>
        <td>
          $body$
        </td>
      </tr>
      <tr>
        <td colspan="2">
          $footer()$
        </td>
      </tr>
    </table>
  </body>
</html>

Again, this template will call another template named header.st. It will do the same for the footer and the menu : each of these layout part is defined in a separate file. The $body$ expression tells the template that it must receive the body as an attribute (see the home.st above).

The welcome body (/WEB-INF/welcome.st)

This is the body of the home page defined in home.st :

<div><h1>Hello, dude !!!</h1></div>

The Header (/WEB-INF/header.st)

<div><h1>this is the header</h1></div>

The Menu (/WEB-INF/menu.st)

  <div>
    <h1>
       <ul>  
         <li>Menu item 1</li>  
         <li>Menu item 2</li>  
         <li>Menu item 3</li>  
         <li>Menu item 4</li>  
         <li>Menu item 5</li>  
         <li>Menu item 6</li>
       </ul>     
    </h1>
  </div>  

The Footer (/WEB-INF/footer.st)

<div><h1>this is the footer</h1></div>

Compiling and launching the web application under tomcat, and then calling http://localhost:8080/stringtemplate/home.st produces the following output :
stringtemplate-example1

With the layout already defined, coding the about page becomes easy : i must create the about page itself, and the body of that page. Let’s start by the page which call the layout :

The about page (/WEB-INF/about.st)

$layout/defaultLayout(body=company_description())$

And the body of that page (WEB-INF/company_description.st)

<h1>About us</h1>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. <br/>
Morbi tempus mauris condimentum arcu. Nunc in quam. Vivamus<br/>
 quis quam sed tortor euismod pellentesque. Nulla lobortis. <br/>
Donec urna metus, adipiscing vel, iaculis vel, congue at, odio. <br/>
Nullam et dolor id tortor tempor gravida. Curabitur eleifend<br/>

I now have two pages that shares the same layout. It would be easy for an entire application to reuse that layout without suffering of a “copy and paste” madness.

Now that all is working, let’s play with the files a little bit. The first thing I’d like to do is to put my layout and its parts in a separate folder. Moving them in WEB-INF/layout force me to modify the two pages :

home.st

$layout/defaultLayout(body=welcome())$

about.st

$layout/defaultLayout(body=company_description())$

Adding to this, I also must modify the layout file (/WEB-INF/layout/defaultLayout.st). This is because, calling $header()$, $menu()$ or $footer()$ will no longer be possible. I have to call all these parts using the correct leading path : $layout/header()$, $layout/menu()$, $layout/footer()$. So here is the new listing of defaultLayout.st :

layout/defaultLayout.st

<html>
  <body style="width:100%;height:100%">
    <table border="1" cellspacing="0" cellpadding="0" style="width:100%;height:100%">
      <tr>
        <td colspan="2">
          $layout/header()$
        </td>
      </tr>
      <tr>
        <td>
          $layout/menu()$
        </td>
        <td>
          $body$
        </td>
      </tr>
      <tr>
        <td colspan="2">
          $layout/footer()$
        </td>
      </tr>
    </table>
  </body>
</html>

Doing this made me realize one thing : always reference a template using the complete path starting from the directory associated with the StringTemplateGroup in action (see the servlet code). Here my template group as been associated with the WEB-INF directory of the application, making all my template references relative to this directory.

Accessing scoped variables.

In a JSP solution, I would have access to a page, request, session and application scopes in which I can read variables. My basic implementation of the StringTemplateServlet doesn’t make it possible for the template files to access scopes. Let’s modify the servlet so I can access request and session variables from my template files :

package sample;

import javax.servlet.*;
import javax.servlet.http.*;

import org.antlr.stringtemplate.*;
import java.io.*;
import java.util.*;

public class StringTemplateServlet extends HttpServlet {
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
    PrintWriter out = response.getWriter();
    ServletContext context = getServletContext();
    String templatePath = context.getRealPath("/WEB-INF");
    StringTemplateGroup group = new StringTemplateGroup("underwebinf", templatePath);
    // removes the leading "/" and the trailing ".st"    
    String servletPath = request.getServletPath().substring(1,request.getServletPath().lastIndexOf(".st"));
    StringTemplate page = group.getInstanceOf(servletPath);
    page.setAttribute("session", getSessionMap(request.getSession()));
    page.setAttribute("request", getRequestMap(request));
    request.getSession().setAttribute("user", "dude");
    out.print(page);
  }

  private Map getSessionMap(HttpSession session) {
    Map sessionMap = new HashMap();
    for(Enumeration names = session.getAttributeNames();names.hasMoreElements();) {
      String attributeName = (String) names.nextElement();      
      sessionMap.put(attributeName, session.getAttribute(attributeName));
    }
    return sessionMap;
  }  
  
  private Map getRequestMap(HttpServletRequest request) {
    Map requestMap = new HashMap();
    for(Enumeration names = request.getAttributeNames(); names.hasMoreElements();) {
      String attributeName = (String) names.nextElement();
      requestMap.put(attributeName, request.getAttribute(attributeName));
    }
    return requestMap;
  }

}

Each scope (request and session) is represented by a map placed among the template’s attributes. Now the welcome file can access the newly created session scoped user attribute :

/WEB-INF/welcome.st

<div><h1>Hello, $session.user$ !!!</h1></div>

All executed templates in the chain could have used $session.user$ to access the “user” session variable. As an example, it would have been possible to print the current user on each page, as part of the layout.

Conclusion

StringTemplate can play the role of the view in a J2EE web application, and can be used as a replacement for JSP/JSTL. It is crafted to answer the need of strict separation between the business and the display logic.

This example was little compared to what this API can do. I encourage those who read my post to take a look at the StringTemplate documentation…. But I’m sure you already did ;)

14 Comments »

  1. Hi Richard,

    Thanks a lot for this tutorial… The lines 12 and 13 from the Implementation section saved me!!!
    I’ve spent 4 days seeking for this solution. I’m a PHP programmer starting Java development and I’m developing a JSP framework.

    Congrats!
    Added to my delicious!

    Thanks a lot!

    Cya!

    Comment by Ramiro Varandas Jr — March 29, 2009 @ 6:57 am | Reply

  2. Great site this richardbarabe.wordpress.com and I am really pleased to see you have what I am actually looking for here and this this post is exactly what I am interested in. I shall be pleased to become a regular visitor :)

    Comment by Updapysless — April 5, 2009 @ 5:18 pm | Reply

  3. hi this is very nice document i would implement it in my site thanks.

    ravendra

    Comment by ravendra kumar — June 11, 2009 @ 3:29 pm | Reply

  4. Thank you for your nice tutorial. It really helps me make maintainable template web sites.

    Thanks
    Sungho

    Comment by Sungho Maeung — July 20, 2009 @ 11:08 am | Reply

  5. sir
    plz tell me how can i run my first program in StringTemplate

    with regards
    Bebin Mishra

    Comment by Bebin Mishra — July 29, 2009 @ 5:42 am | Reply

  6. Thanks,good example to understand the concept

    Comment by suraj — March 24, 2010 @ 7:09 am | Reply

  7. Hi,

    I’m new in Spring and I have a seroius (for me of course) problem with my project:

    I’m developing a web application using:
    1) Spring + MVC
    2) Hibernate
    3) StringTemplate

    I’ve built a layout using ST from a tutorial. Extensions of the layout files are .*st.
    Everything was good until I nedded to use a Spring form tag.
    I noticed that tags are not proccessed, they are just printed on a page.
    Later I noticed in Firefox that Java directives ( are printed too.

    It seems that I missed something in web.xml. What’s wrong in the configuration?

    Thanks,
    jolsys

    Here’s my web.xml:
    [CODE]

    org.springframework.web.context.ContextLoaderListener

    contextConfigLocation

    /WEB-INF/jshop-servlet.xml,
    /WEB-INF/applicationContext-security.xml

    encoding-filter

    org.springframework.web.filter.CharacterEncodingFilter

    encoding
    UTF-8

    encoding-filter
    /*

    springSecurityFilterChain
    org.springframework.web.filter.DelegatingFilterProxy

    springSecurityFilterChain
    /*

    jshop
    org.springframework.web.servlet.DispatcherServlet
    1

    jshop
    *.html

    index.html

    [/CODE]

    [B]jshop-servlet.xml:[/B]

    [CODE]



    indexController
    ProductController

    [/CODE]

    Comment by joll — May 20, 2010 @ 6:12 pm | Reply

  8. Thanks for this article, the tutorial is great. And the introduction made me understand a few things.

    You say: “StringTemplate forbids any assignments, making it harder to put what should be business or controller logic in the view.”
    And sudeenly I have realized that is the reason why people use template mechanisms.

    I was always wondering why do I need StringTemplate when I have JSP. And now I can see that. The more restrictions we put on a programmer, the better pogramm he will write ;-). And that – in my opinion – is probably the reason why the COBOL language is not a dead language and it powers our financial institutions for decades! The power of restrictions! What a good title for an article. I see the flame war starting here.

    Anyway thank you. Really great post.

    Comment by bartoszrybacki — February 2, 2012 @ 11:09 am | Reply

  9. This tutorial is great, do you know of a similar tutorial that relates to StringTemplate 4? Im new to StringTemplate, and using StringTemplate 4 I can’t seem to get it all to work at all.

    Comment by Jacob — June 7, 2012 @ 1:20 am | Reply

  10. Hi Richard. I love ur blogg man. I now have a fully idea with StringTemplate and the example is clear, the configuration and the output.

    Comment by Elias — September 14, 2012 @ 6:44 pm | Reply

  11. This is a topic that is close to my heart… Cheers!

    Where are your contact details though?

    Comment by laws on cold calling — January 18, 2013 @ 1:54 pm | Reply

  12. If some one desires expert view concerning running a blog after
    that i recommend him/her to go to see this weblog, Keep up the pleasant job.

    Comment by hot And cold Lyrics — January 22, 2013 @ 11:14 pm | Reply

  13. Wow, incredible blog format! How long have you been blogging for?
    you made blogging glance easy. The full look of your site is great, as
    well as the content material!

    Comment by sales tipping point chicago — May 28, 2013 @ 2:35 am | Reply

  14. I use ST4 and the following syntax seems no longer work
    $defaultLayout(body=welcome())$ (BTW, it should be $layout(body=welcome())$
    it complains that the attribute body is missing and does not load the body attribute.
    Could you verify if the above syntax is still working with ST4.
    Thanks very much
    Paul

    Comment by Paul — January 15, 2015 @ 4:26 pm | Reply


RSS feed for comments on this post. TrackBack URI

Leave a comment

Blog at WordPress.com.